#include "src/ltarena.h"
#include "src/ltlog.h"
#include "src/ltpanic.h"

typedef struct ltarena__Header_ {
    uint32_t block_id_;
    uint32_t size_;
    uint32_t magic_;
    ltarena__Header* header_;
} ltarena__Header;

/* 字符串"ltarena"的ASCII值的和。*/
static const uint32_t g_Magic = 743;

static ltarena__Header*
AllocWithHeader(ltarena_Arena* arena, uint32_t size) {

    ltarena__Header* hdr = (ltarena__Header*)lt_Malloc(
        size + sizeof(ltarena__Header));

    hdr->block_id_ = arena->hptrs_.len;
    hdr->size_ = size;
    hdr->magic_ = g_Magic;
    hdr->header_ = hdr;

    ltlog_Log4(Debug, "arena alloc", "header:", Ptr(hdr), "size:", U32(size),
               "magic:", U32(g_Magic), "block id:", U32(hdr->block_id_));

    lta_Append(arena->hptrs_, hdr);

    return hdr;
}

static ltarena__Header*
GetHeader(void* ptr) {
    return (ltarena__Header*)ptr - 1;
}

void
ltarena_Free(ltarena_Arena* arena) {
    lta_ForEach(i, arena->hptrs_) {
        lt_Free(arena->hptrs_.data[i], arena->hptrs_.data[i]->size_);
    }
    lta_DeInit(arena->hptrs_);
}

void*
ltarena_Alloc(ltarena_Arena* arena, uint32_t size) {
    void* p = AllocWithHeader(arena, size) + 1;
    ltlog_Log1(Debug, "arena alloc", "result:", Ptr(p));
    return p;
}

void*
ltarena_Realloc(ltarena_Arena* arena, void* ptr, uint32_t size) {
    if (ptr == NULL) {
        return ltarena_Alloc(arena, size);
    }

    ltarena__Header* hdr = GetHeader(ptr);
    if (hdr->magic_ != g_Magic || hdr->block_id_ > arena->hptrs_.len
        || hdr->header_ != hdr) {
        lt_Panic("bad memory into arena");
    }

    uint32_t block_id = hdr->block_id_;

    hdr = (ltarena__Header*)lt_Realloc(arena->hptrs_.data[block_id],
                                       size + sizeof(ltarena__Header));
    hdr->block_id_ = block_id;
    hdr->magic_ = g_Magic;
    hdr->size_ = size;

    arena->hptrs_.data[block_id] = hdr;

    return hdr + 1;
}
